// ----------------------------------------------------------------------------
// hash_murmur.cpp
// ----------------------------------------------------------------------------
// from https://sites.google.com/site/murmurhash/
#include <fw/base/core/stdinteger.h>
#include <fw/base/core/assertion.h>
#include <fw/base/runtime/_IMPL/hash_murmur.h>
#include <stddef.h>

namespace fw
{
	namespace murmur
	{
		uint32_t		HashValueInternal32(const void* key, size_t len, uint32_t seed)
		{
			FW_ASSERT(key && len>0);
			// 'm' and 'r' are mixing constants generated offline.
			// They're not really 'magic', they just happen to work well.
			
			const unsigned int m = FW_MURMUR_M_32;
			const int r = FW_MURMUR_R_32;
			
			// Initialize the hash to a 'random' value
			
			unsigned int h = seed ^ static_cast<uint32_t>(len);
			
			// Mix 4 bytes at a time into the hash
			
			const unsigned char * data = (const unsigned char *)key;
			
			while(len >= 4)
			{
				unsigned int k = *(unsigned int *)data;
				
				k *= m;
				k ^= k >> r;
				k *= m;
				
				h *= m;
				h ^= k;
				
				data += 4;
				len -= 4;
			}
            
			// Handle the last few bytes of the input array
			
			switch(len)
			{
				case 3: h ^= data[2] << 16;
				case 2: h ^= data[1] << 8;
				case 1: h ^= data[0];
					h *= m;
			}
            
			// Do a few final mixes of the hash to ensure the last few
			// bytes are well-incorporated.
			
			h ^= h >> 13;
			h *= m;
			h ^= h >> 15;
			
			return h;			
		}
		
		uint64_t		HashValueInternal64(const void* key, size_t len, uint64_t seed)
		{
			FW_ASSERT(key && len>0);
			const uint64_t m = FW_MURMUR_M_64;
			const int r = 47;
			
			uint64_t h = seed ^ (len * m);
            
			const uint64_t * data = (const uint64_t *)key;
			const uint64_t * end = data + (len/8);
			
			while(data != end)
			{
				uint64_t k = *data++;
				
				k *= m;
				k ^= k >> r;
				k *= m;
				
				h ^= k;
				h *= m;
			}
			
			const unsigned char * data2 = (const unsigned char*)data;
			
			switch(len & 7)
			{
				case 7: h ^= uint64_t(data2[6]) << 48;
				case 6: h ^= uint64_t(data2[5]) << 40;
				case 5: h ^= uint64_t(data2[4]) << 32;
				case 4: h ^= uint64_t(data2[3]) << 24;
				case 3: h ^= uint64_t(data2[2]) << 16;
				case 2: h ^= uint64_t(data2[1]) << 8;
				case 1: h ^= uint64_t(data2[0]);
					h *= m;
			};
			
			h ^= h >> r;
			h *= m;
			h ^= h >> r;
			
			return h;
		}
		
	} // namespace murmur
} // namespace fw

